iT邦幫忙

2021 iThome 鐵人賽

DAY 14
0
自我挑戰組

馬克的軟體架構小筆記系列 第 14

30-14 之 Domain Layer - Service

  • 分享至 

  • xImage
  •  

在談論完書中提的組織 domain 的三種模式 :

  • Transaction Script
  • Domain Model
  • Table Module

接下來我們要來提書中將他放在 domain 章節的『 Service 』。

什麼是 Service 呢 ?

先提一下,我前面有幾個範例裡有寫到 service,但嚴格來說這不是書中所提 service 層意思,之前範例是指商業邏輯用的地方,我範例都寫 service,應該不少人的定義也是相同,但書中不同。

從書中抓出的一張圖應該就可以知道, service 在書中實際上的位置如下,從下圖可以簡單的定義 :

Service Layer 會在 domain model 的前,但他還是屬於 domain 層,它負責處理業務的工作流處理。

我當初在讀 domain model 那章節我事實上一直有地方卡到,那就是 domain model 可以當做以每個業務為單位,然後內包屬性與行為,那假設有個業務為『 這個月當用戶註冊後,要送 Coupon 卷 』,這個業務要如何處理呢 ?

以 domain model 應該是會分 :

  • User.register
  • Coupon.create

但問題是,同時呼叫這兩個方法的地方是那 ? 對 ~ 這個地方就是 『 Service Layer 』。

https://ithelp.ithome.com.tw/upload/images/20210929/20089358rS4OQwfqbo.png
圖片來源: 《 企業應用架構模式 》

書中事實上還有提到幾個 Service Layer 的使用重點 :

  • 如果你的業務有多種用戶。
  • 同時業務涉及多種事務操作

我事實上在思考 ~ 有時後我們 Web 開發時,有分所謂的 controller、service 會不會嚴格來說,以書中定義 controller ⇒ service 而 service ⇒ domain model 呢 ? 因為我相信不少人 controller 裡面,是寫這個 api 要處理的事情 ~ 對吧 ?

不過我這裡不太建議,以我自已的標準這三個實際上該做的事情為 :

  • controller : 處理請求,並且將 service 回傳的東西組裝成這個 api 所需要的。
  • service : 處理整個業務所需要的工作流。
  • domain : 專門處理每個 domain 的工作內容。

範例

這裡給個簡單的範例,業務需求為 :

當用戶註冊時,會給一張 Coupon ( 折扣卷 ),且寄出歡迎信

簡單來說該業務可以分為三個部份 :

  • 用戶註冊
  • 給 Coupon
  • 寄出歡迎信

其中有幾點要注意 :

  • UserService 可以想成某個 app 要求使用者註冊時,會首先開始處理的地方。
  • UserService 會繼承 App Service,這裡我是模仿書中範例寫的,事實上不一定要這樣做,這裡可以想成 application service 是一個 container 然後可以從裡面拿 『 這個 application 所提供的外部服務 』就好
  • 由於 email 服務為『 該 application 所提供的外部服務 』因此要從 ApplicationService 取得服候實體。
  • UserService 的 register 就是處理『 業務需求 』的整個工作流。
class ApplicationService {
    getEmailGateWay(): IEmailGateway{
        return new EmailGatewayService() 
    }
}

interface IEmailGateway{
    sendEmailMessage(toEmail: string, subject: string, body: any): void
}

class EmailGatewayService implements IEmailGateway{
    sendEmailMessage(toEmail: string, subject: string, body: any): void{
        console.log('取得該 application 的寄信服務的 config,然後在寄信')
    }
}

class UserService extends ApplicationService{
    register(username: string, email: string): void{
        const userDomainModel = new UserDomainModel({})
        const couponDomainModel = new CouponDomainModel({}) 
        // 建立使用者
        const user = new User(username, email)
        userDomainModel.create(user)
        // 建立第一次註冊 Coupon
        const firstCoupon = new Coupon('First Coupon', 100)
        couponDomainModel.setCoupon(firstCoupon)
        couponDomainModel.sendToUser(user.id)

        // 寄歡迎信
        this.getEmailGateWay().sendEmailMessage('mark@gmail.com', 'First Register',  {})
    }
}

class User{
    username: string
    email: string
    constructor(username: string, email: string){
        this.username = username
        this.email= email
    }
}

class Coupon{
    title: string 
    discount: number
    constructor(title: string, discount: number){
        this.title = title
        this.discount = discount
    }
}

class UserDomainModel{
    userDao: any
    constructor(userDao: any){
        this.userDao = userDao
    }
    create(user: User){
        this.userDao.create(user)
    }
}

class CouponDomainModel{
    couponDao: any
    coupon: Coupon
    constructor(couponDao: any){
        this.couponDao = couponDao
    }

    setCoupon(coupon){
        this.coupon = coupon
    }
    sendToUser(userId){
        console.log('Save to user coupon table')
        this.couponDao.save(this.coupon, userId)
    }
}

小總結

這個知識點可以用來解釋什麼現象

有時後在寫業務需求時,我發現真的很難每一個 domain 都定義清楚,像我剛剛提到的範例 :

當用戶註冊時,會給一張 Coupon ( 折扣卷 ),且寄出歡迎信

如果我們將『 用戶註冊時,會給一張 Coupon 』,寫在 User Domain Model 中,但如果其它 application 不需要給一張 coupon 時,要怎麼辦呢 ? 這時有了 service 專門處理工作流的地方真的方便不少。

這個知識點可以和以前的什麼知識連結呢 ?

事實上我寫到現在,單純的使用 domain model 來寫,我還真想不到如果寫,但多了一個 service layer 就真的方便多了…

然後在看《 搞笑談軟工-PoEEA之Server Layer 這篇文章 』》有提到一句話 :

傳統階層式架構所說的 Service Layer,就是Clean Architecture裡面的 Use Case Layer。Teddy現在覺得Use Case Layer比較具體,因為Service這個字有很多種含意,因此用Service Layer來代表應用程式所提供功能或服務的邊界,好像有點那麼不是很直覺

這句話我覺得有理,給其它人參考看看。

我要如何運用這個知識點 ?

  • 嘗試更明確的思考,什麼時後寫在 service layer,而什麼時後寫在 domain layer。
  • 還有書中的 EmailGateway 的寫法我覺得是不錯的方向,這在之後的架構可以考慮這樣拆。

參考資料


上一篇
30-13 之 Domain Layer - Table Module
下一篇
30-15 之 DataSource Layer - TableDataGateway
系列文
馬克的軟體架構小筆記29
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言